package com.chandon.juc.thread_communication.SyncContainer;

import java.util.LinkedList;

/**
 * 一个固定量的同步容器，put get getCount方法，支持2个生产者线程与10个消费者线程的阻塞调用。
 * 使用 wait/notify notifyAll实现
 *
 * 缺陷：
 * 由于使用了同步方法，所有线程不管是生产者还是消费者都阻塞在了this对象上，notifyAll无法准确判断唤醒生产者还是消费者。
 * 可使用Condition改造，见SyncContainerUserCondition
 *
 *
 * @author Dong
 */

public class SyncContainerUseWaitAndNotify<T> {

	private final LinkedList<T> list = new LinkedList<>();
	private final int maxSize = 10;
	int count = 0;

	/**
	 * count++线程不安全，所以使用synchronized
	 *
	 * @param t
	 */
	public synchronized void put(T t) {

		/*
			用while而不适用if的原因：
			多线程情况下要随时判断是否已经装满了
			如果使用if，判断了一次
		 */
		while (list.size() == maxSize) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
		}

		list.add(t);
		count++;
		//	叫醒其他阻塞在while中的线程
		this.notifyAll();

	}

	public synchronized T get() {

		while (list.size() == 0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
		}
		count--;
		T t = list.removeFirst();
		this.notifyAll();
		return t;
	}

	public synchronized int getCount() {
		return list.size();
	}

	public static void main(String[] args) {
		SyncContainerUseWaitAndNotify<Object> syncContainer = new SyncContainerUseWaitAndNotify<>();

		/* 生产者: 2个线程，每个线程Put 50个 */
		for (int i = 0; i < 2; i++) {
			new Thread(() -> {
				for (int j = 0; j < 50; j++) {
					syncContainer.put(Thread.currentThread().getName() + ":" + j);
				}
			}, "Thread-producer" + i).start();
		}

		/* 消费者：10个线程 */
		for (int i = 0; i < 10; i++) {
			new Thread(() -> {
				for (int j = 0; j < 10; j++) {
					System.out.println(Thread.currentThread().getName() + ":" + syncContainer.get());
				}
			}, "Thread-consumer" + i).start();
		}

	}


}
